home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Special 18 / AMIGAplus Sonderheft 18 (1999)(ICP)(DE)[!].iso / PD / Spiele / lazymines / lazymines_src / field.c < prev    next >
C/C++ Source or Header  |  1999-01-03  |  27KB  |  1,026 lines

  1. /*
  2.  * field.c
  3.  * =======
  4.  * Implements the minefield.
  5.  *
  6.  * Copyright (C) 1994-1998 Håkan L. Younes (lorens@hem.passagen.se)
  7.  */
  8.  
  9. #include <math.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13.  
  14. #include <dos/dos.h>
  15. #include <exec/memory.h>
  16. #include <exec/types.h>
  17. #include <proto/exec.h>
  18. #include <proto/graphics.h>
  19. #include <intuition/imageclass.h>
  20. #include <proto/intuition.h>
  21.  
  22. #include "localize.h"
  23. #include "display_globals.h"
  24. #include "layout.h"
  25. #include "images.h"
  26. #include "counter.h"
  27. #include "game.h"
  28. #include "field.h"
  29.  
  30.  
  31. struct field {
  32.    struct RastPort  *rp;
  33.    WORD              left;
  34.    WORD              top;
  35.    UBYTE             rows;
  36.    UBYTE             columns;
  37.    UWORD             mines;
  38.    UBYTE            *data;
  39.    UWORD            *zeros;
  40.    UWORD             swept;
  41. };
  42.  
  43.  
  44. #define MINE     0x09
  45. #define LOCKED   0x10
  46. #define WARNED   0x20
  47. #define SWEPT    0x40
  48. #define PATH     0x80
  49.  
  50. #define FIELD(f, r, c)          ((f)->data[(r) * (f)->columns + (c)])
  51. #define SET_FIELD(f, r, c, v)   (FIELD ((f), (r), (c)) = (v))
  52.  
  53. #define CELL_VAL(f, r, c)       (FIELD ((f), (r), (c)) & 0x0F)
  54. #define IS_ZERO(f, r, c)        (CELL_VAL ((f), (r), (c)) == 0)
  55. #define IS_MINE(f, r, c)        (CELL_VAL ((f), (r), (c)) == MINE)
  56. #define IS_LOCKED(f, r, c)      (FIELD ((f), (r), (c)) & LOCKED)
  57. #define IS_WARNED(f, r, c)      (FIELD ((f), (r), (c)) & WARNED)
  58. #define IS_SWEPT(f, r, c)       (FIELD ((f), (r), (c)) & SWEPT)
  59. #define IS_PATH(f, r, c)        (FIELD ((f), (r), (c)) & PATH)
  60. #define IS_SWEEPABLE(f, r, c)   (!(FIELD ((f), (r), (c)) & (LOCKED | SWEPT)))
  61.  
  62. #define LOCK_CELL(f, r, c)      (FIELD ((f), (r), (c)) |= LOCKED)
  63. #define WARN_CELL(f, r, c)      (FIELD ((f), (r), (c)) |= WARNED)
  64. #define UNLOCK_CELL(f, r, c)    (FIELD ((f), (r), (c)) &= ~(LOCKED | WARNED))
  65. #define SWEEP_CELL(f, r, c)     (FIELD ((f), (r), (c)) |= SWEPT)
  66. #define PATH_CELL(f, r, c)      (FIELD ((f), (r), (c)) |= PATH)
  67.  
  68. #define POSINFIELD(f, r, c)     ((c) + (r) * (f)->columns)
  69. #define POS2ROW(f, p)           ((p) / (f)->columns)
  70. #define POS2COL(f, p)           ((p) % (f)->columns)
  71.  
  72. #define FIELDWIDTH(f)           ((f)->columns * cell_w + 2 * LINEWIDTH)
  73. #define FIELDHEIGHT(f)          ((f)->rows * cell_h + 2 * LINEHEIGHT)
  74.  
  75.  
  76. #define RAISEDBOX     0
  77. #define RECESSEDBOX   1
  78. #define THINBOX       2
  79.  
  80.  
  81. static void
  82. draw_box(
  83.    struct RastPort  *rp,
  84.    WORD              left,
  85.    WORD              top,
  86.    UWORD             width,
  87.    UWORD             height,
  88.    UBYTE             type)
  89. {
  90.    if (type == THINBOX)
  91.    {
  92.       SetAPen (rp, gui_pens[SHADOWPEN]);
  93.       Move (rp, left, top);
  94.       Draw (rp, left + width - 1, top);
  95.       Draw (rp, left + width - 1, top + height - 1);
  96.       Draw (rp, left, top + height - 1);
  97.       Draw (rp, left, top);
  98.       SetAPen (rp, gui_pens[BACKGROUNDPEN]);
  99.       Move (rp, left + 1, top + 1);
  100.       Draw (rp, left + 1, top + height - 2);
  101.       Move (rp, left + width - 2, top + 1);
  102.       Draw (rp, left + width - 2, top + height - 2);
  103.    }
  104.    else
  105.    {
  106.       SetAPen(rp, gui_pens[((type == RAISEDBOX) ? SHINEPEN : SHADOWPEN)]);
  107.       Move (rp, left + width - 2, top);
  108.       Draw (rp, left, top);
  109.       Draw (rp, left, top + height - 1);
  110.       Move (rp, left + 1, top + height - 2);
  111.       Draw (rp, left + 1, top + 1);
  112.       SetAPen(rp, gui_pens[((type == RAISEDBOX) ? SHADOWPEN: SHINEPEN)]);
  113.       Move (rp, left + 1, top + height - 1);
  114.       Draw (rp, left + width - 1, top + height - 1);
  115.       Draw (rp, left + width - 1, top);
  116.       Move (rp, left + width - 2, top + 1);
  117.       Draw (rp, left + width - 2, top + height - 2);
  118.    }
  119. }
  120.  
  121.  
  122. static void
  123. draw_cell (
  124.    struct RastPort  *rp,
  125.    WORD              left,
  126.    WORD              top,
  127.    UBYTE             value)
  128. {
  129.    draw_box (rp, left, top, cell_w, cell_h,
  130.              ((value & SWEPT) ? THINBOX : RAISEDBOX));
  131.    SetAPen (rp, gui_pens[BACKGROUNDPEN]);
  132.    RectFill (rp, left + LINEWIDTH, top + LINEHEIGHT,
  133.              left + cell_w - LINEWIDTH - 1, top + cell_h - LINEHEIGHT - 1);
  134.    if (value & SWEPT)
  135.    {
  136.       value &= 0x0F;
  137.       if (value == MINE)
  138.          DrawImageState (rp, mine_image, left, top, IDS_NORMAL, NULL);
  139.       else
  140.       {
  141.          if (value > 0)
  142.          {
  143.             char   ch = value + '0';
  144.             
  145.             SetAPen (rp, (game_pens[value - 1] != -1 && display_colors) ?
  146.                      game_pens[value - 1] : gui_pens[TEXTPEN]);
  147.             Move (rp, left + (cell_w - rp->TxWidth) / 2,
  148.                   top + (cell_h - rp->TxHeight) / 2 + rp->TxBaseline);
  149.             Text (rp, &ch, 1);
  150.          }
  151.       }
  152.    }
  153.    else if (value & LOCKED)
  154.    {
  155.       if (value & WARNED)
  156.       {
  157.          SetAPen (rp, gui_pens[TEXTPEN]);
  158.          Move (rp, left + (cell_w - rp->TxWidth) / 2,
  159.                top + (cell_h - rp->TxHeight) / 2 + rp->TxBaseline);
  160.          Text (rp, "?", 1);
  161.       }
  162.       else
  163.          DrawImageState (rp, flag_image, left, top, IDS_NORMAL, NULL);
  164.    }
  165. }
  166.  
  167.  
  168. static UBYTE
  169. count_neighbors (
  170.    field_ptr   field,
  171.    WORD        row,
  172.    WORD        col,
  173.    UBYTE       filter)
  174. {
  175.    register WORD    r, c;
  176.    register UBYTE   count = 0;
  177.    
  178.    
  179.    for (r = row - 1; r <= row + 1; ++r)
  180.    {
  181.       for (c = col - 1; c <= col + 1; ++c)
  182.       {
  183.          if (field_inside (field, r, c) && !(r == row && c == col) &&
  184.              (FIELD (field, r, c) & filter) == filter)
  185.          {
  186.             ++count;
  187.          }
  188.       }
  189.    }
  190.    
  191.    return count;
  192. }
  193.  
  194.  
  195. static void
  196. avoid_trivial (
  197.    field_ptr   field)
  198. {
  199.    register WORD   r, c, rr, cc;
  200.    UBYTE           row0, col0;
  201.    UWORD           pos, next;
  202.    BOOL            trivial = FALSE;
  203.    
  204.    
  205.    if (CELL_VAL (field, 0, 0) == 0)
  206.    {
  207.       SWEEP_CELL (field, 0, 0);
  208.       pos = next = 0;
  209.       field->zeros[next++] = POSINFIELD (field, 0, 0);
  210.       while (pos < next && !trivial)
  211.       {
  212.          row0 = POS2ROW (field, field->zeros[pos]);
  213.          col0 = POS2COL (field, field->zeros[pos]);
  214.          for (r = row0 - 1; r <= row0 + 1; ++r)
  215.          {
  216.             for (c = col0 - 1; c <= col0 + 1; ++c)
  217.             {
  218.                if (field_inside (field, r, c) &&
  219.                    !IS_SWEPT (field, r, c))
  220.                {
  221.                   SWEEP_CELL (field, r, c);
  222.                   if (r == field->rows - 1 && c == field->columns - 1)
  223.                      trivial = TRUE;
  224.                   
  225.                   if (IS_ZERO (field, r, c))
  226.                       field->zeros[next++] = POSINFIELD (field, r, c);
  227.                }
  228.             }
  229.          }
  230.          ++pos;
  231.       }
  232.                
  233.       for (r = 0; r < field->rows; ++r)
  234.          for (c = 0; c < field->columns; ++c)
  235.             if (IS_SWEPT (field, r, c))
  236.                FIELD (field, r, c) &= ~SWEPT;
  237.    }
  238.    
  239.    if (trivial)
  240.    {
  241.       row0 = col0 = r = 0;
  242.       if (!IS_PATH (field, 0, 1))
  243.          r |= 1;
  244.       if (!IS_PATH (field, 1, 0))
  245.          r |= 2;
  246.       if (!IS_PATH (field, 1, 1))
  247.          r |= 4;
  248.       if (r == 0)
  249.       {
  250.          if ((int)(2 * drand48 ()) == 0)
  251.             r |= 1;
  252.          else
  253.             r |= 2;
  254.       }
  255.       
  256.       while (row0 == 0 && col0 == 0)
  257.       {
  258.          rr = 3 * drand48 ();
  259.          if (r & (1 << rr))
  260.          {
  261.             row0 = (rr == 0) ? 0 : 1;
  262.             col0 = (rr == 1) ? 0 : 1;
  263.          }
  264.       }
  265.       
  266.       for (r = 0; r < field->rows && trivial; ++r)
  267.       {
  268.          for (c = 0; c < field->columns && trivial; ++c)
  269.          {
  270.             if (IS_MINE (field, r, c))
  271.             {
  272.                trivial = FALSE;
  273.                
  274.                SET_FIELD (field, r, c, 0);
  275.                for (rr = r - 1; rr <= r + 1; ++rr)
  276.                {
  277.                   for (cc = c - 1; cc <= c + 1; ++cc)
  278.                   {
  279.                      if (field_inside (field, rr, cc) &&
  280.                          !IS_MINE (field, rr, cc))
  281.                      {
  282.                         SET_FIELD (field, rr, cc,
  283.                                    count_neighbors (field, rr, cc, MINE));
  284.                      }
  285.                   }
  286.                }
  287.             }
  288.          }
  289.       }
  290.                
  291.       SET_FIELD (field, row0, col0, MINE);
  292.       for (rr = row0 - 1; rr <= row0 + 1; ++rr)
  293.       {
  294.          for (cc = col0 - 1; cc <= col0 + 1; ++cc)
  295.          {
  296.             if (field_inside (field, rr, cc) && !IS_MINE (field, rr, cc))
  297.             {
  298.                SET_FIELD (field, rr, cc,
  299.                           count_neighbors (field, rr, cc, MINE));
  300.             }
  301.          }
  302.       }
  303.    }
  304. }
  305.  
  306.  
  307. static BOOL
  308. generate_path (
  309.    field_ptr   field)
  310. {
  311.    register WORD       r, c;
  312.    UBYTE               row, col;
  313.    UWORD               path_count, next_pos, possible_pos[8];
  314.    UBYTE               count, try_count = 0;
  315.    BOOL                done, failure = TRUE;
  316.    
  317.    char                msg[81];
  318.    ULONG               char_count;
  319.    struct TextExtent   te;
  320.    
  321.    
  322.    SetAPen (field->rp, gui_pens[BACKGROUNDPEN]);
  323.    RectFill (field->rp, field->left + LINEWIDTH, field->top + LINEHEIGHT,
  324.              field->left + FIELDWIDTH (field) - LINEWIDTH - 1,
  325.              field->top + FIELDHEIGHT (field) - LINEHEIGHT - 1);
  326.    sprintf (msg, localized_string (MSG_GENERATE_PATH), try_count + 1);
  327.    char_count = TextFit (field->rp, msg, strlen (msg), &te, NULL, 1,
  328.                          FIELDWIDTH (field) - 2 * LINEWIDTH,
  329.                          FIELDHEIGHT (field) - 2 * LINEHEIGHT);
  330.    SetAPen (field->rp, gui_pens[TEXTPEN]);
  331.    
  332.    while (failure)
  333.    {
  334.       Move (field->rp,
  335.             field->left + (FIELDWIDTH (field) - te.te_Width) / 2,
  336.             field->top + field->rp->TxBaseline +
  337.             (FIELDHEIGHT (field) - te.te_Height) / 2);
  338.       Text (field->rp, msg, char_count);
  339.       
  340.       done = FALSE;
  341.       row = col = 0;
  342.       path_count = 1;
  343.       PATH_CELL (field, row, col);
  344.       while (!done)
  345.       {
  346.          count = 0;
  347.          for (r = row - 1; r <= row + 1; ++r)
  348.          {
  349.             for (c = col - 1; c <= col + 1; ++c)
  350.             {
  351.                if (field_inside (field, r, c) && !IS_PATH (field, r, c) &&
  352.                    !(col == 0 && r < row) && !(row == 0 && c < col) &&
  353.                    !(col == field->columns - 1 && r < row) &&
  354.                    !(row == field->rows - 1 && c < col))
  355.                {
  356.                   possible_pos[count++] = POSINFIELD (field, r, c);
  357.                }
  358.             }
  359.          }
  360.          
  361.          if (count > 0)
  362.          {
  363.             next_pos = possible_pos[(int)(count * drand48 ())];
  364.             row = POS2ROW (field, next_pos);
  365.             col = POS2COL (field, next_pos);
  366.             PATH_CELL (field, row, col);
  367.             ++path_count;
  368.          }
  369.          
  370.          if (count == 0 ||
  371.              (row == field->rows - 1 && col == field->columns - 1))
  372.             done = TRUE;
  373.       }
  374.       
  375.       if (count == 0 ||
  376.           field->rows * field->columns - path_count < field->mines)
  377.       {
  378.          ++try_count;
  379.          sprintf (msg, localized_string (MSG_GENERATE_PATH), try_count + 1);
  380.          
  381.          if (current_level == OPTIONAL_LEVEL && try_count == 100)
  382.             return FALSE;
  383.          
  384.          memset (field->data, 0,
  385.                  field->rows * field->columns * sizeof (*field->data));
  386.       }
  387.       else
  388.          failure = FALSE;
  389.    }
  390.    
  391.    return TRUE;
  392. }
  393.  
  394.  
  395. static void
  396. generate_opening (
  397.    field_ptr   field)
  398. {
  399.    register UBYTE   row, col, row0, col0;
  400.    register WORD    r, c;
  401.    UBYTE            best_row = 0, best_col = 0;
  402.    UWORD            best_count = 0xFFFF, sweep_count, wanted_count;
  403.    UWORD            pos, next;
  404.    
  405.    
  406.    wanted_count = field->rows * field->columns * (auto_opening / 100.0);
  407.    if (wanted_count == 0)
  408.       wanted_count = 1;
  409.    
  410.    for (row = 0; row < field->rows; ++row)
  411.    {
  412.       for (col = 0; col < field->columns; ++col)
  413.       {
  414.          if (!(IS_MINE (field, row, col) || IS_PATH (field, row, col)))
  415.          {
  416.             sweep_count = 1;
  417.             if (CELL_VAL (field, row, col) == 0)
  418.             {
  419.                PATH_CELL (field, row, col);
  420.                pos = next = 0;
  421.                field->zeros[next++] = POSINFIELD (field, row, col);
  422.                while (pos < next)
  423.                {
  424.                   row0 = POS2ROW (field, field->zeros[pos]);
  425.                   col0 = POS2COL (field, field->zeros[pos]);
  426.                   for (r = row0 - 1; r <= row0 + 1; ++r)
  427.                   {
  428.                      for (c = col0 - 1; c <= col0 + 1; ++c)
  429.                      {
  430.                         if (field_inside (field, r, c) &&
  431.                             !IS_PATH (field, r, c))
  432.                         {
  433.                            PATH_CELL (field, r, c);
  434.                            ++sweep_count;
  435.                            
  436.                            if (IS_ZERO (field, r, c))
  437.                               field->zeros[next++] = POSINFIELD (field, r, c);
  438.                         }
  439.                      }
  440.                   }
  441.                   ++pos;
  442.                }
  443.                
  444.                for (r = 0; r < field->rows; ++r)
  445.                   for (c = 0; c < field->columns; ++c)
  446.                      if (IS_PATH (field, r, c) && !IS_ZERO (field, r, c))
  447.                         FIELD (field, r, c) &= ~PATH;
  448.             }
  449.             
  450.             if (abs (sweep_count - wanted_count) <
  451.                 abs (best_count - wanted_count) &&
  452.                 sweep_count < field->rows * field->columns - field->mines)
  453.             {
  454.                best_count = sweep_count;
  455.                best_row = row;
  456.                best_col = col;
  457.             }
  458.          }
  459.       }
  460.    }
  461.    
  462.    reveal_this (field, best_row, best_col);
  463.    counter_update (time_counter, best_count / 4);
  464.    time_on = TRUE;
  465.    timer_start (timer_obj, 0L, 1000000L);
  466. }
  467.  
  468.  
  469. static void
  470. mutate_neighbors (
  471.    field_ptr   field,
  472.    WORD        row,
  473.    WORD        col,
  474.    UBYTE       filter,
  475.    UBYTE       mask)
  476. {
  477.    register WORD   r, c;
  478.    
  479.    
  480.    for (r = row - 1; r <= row + 1; ++r)
  481.    {
  482.       for (c = col - 1; c <= col + 1; ++c)
  483.       {
  484.          if (field_inside (field, r, c) && (r != row || r != col) &&
  485.              FIELD (field, r, c) & filter)
  486.          {
  487.             FIELD (field, r, c) &= ~mask;
  488.          }
  489.       }
  490.    }
  491. }
  492.  
  493.  
  494. static void
  495. sweep_zero (
  496.    field_ptr   field,
  497.    UBYTE       row,
  498.    UBYTE       col)
  499. {
  500.    register WORD   r, c;
  501.    UWORD           pos = 0, next = 0;
  502.    
  503.    
  504.    field->zeros[next++] = POSINFIELD (field, row, col);
  505.    while (pos < next)
  506.    {
  507.       row = POS2ROW (field, field->zeros[pos]);
  508.       col = POS2COL (field, field->zeros[pos]);
  509.       for (r = row - 1; r <= row + 1; ++r)
  510.       {
  511.          for (c = col - 1; c <= col + 1; ++c)
  512.          {
  513.             if (field_inside (field, r, c) && IS_SWEEPABLE (field, r, c))
  514.             {
  515.                SWEEP_CELL (field, r, c);
  516.                ++field->swept;
  517.                draw_cell (field->rp, field->left + LINEWIDTH + c * cell_w,
  518.                           field->top + LINEHEIGHT + r * cell_h,
  519.                           FIELD (field, r, c));
  520.                
  521.                if (IS_ZERO (field, r, c))
  522.                   field->zeros[next++] = POSINFIELD (field, r, c);
  523.             }
  524.          }
  525.       }
  526.       ++pos;
  527.    }
  528. }
  529.  
  530.  
  531. static BOOL
  532. reveal_around (
  533.    field_ptr   field,
  534.    UBYTE       row,
  535.    UBYTE       col)
  536. {
  537.    register WORD   r, c;
  538.    BOOL            success = TRUE;
  539.    
  540.    
  541.    for (r = row - 1; r <= row + 1 && success; ++r)
  542.       for (c = col - 1; c <= col + 1 && success; ++c)
  543.          if (field_inside (field, r, c) && (r != row || c != col))
  544.             success = reveal_this (field, r, c);
  545.    
  546.    return success;
  547. }
  548.  
  549.  
  550. field_ptr
  551. field_init (
  552.    struct RastPort  *rp,
  553.    WORD              left,
  554.    WORD              top,
  555.    UBYTE             rows,
  556.    UBYTE             columns,
  557.    UWORD             mines)
  558. {
  559.    field_ptr   field;
  560.    
  561.    
  562.    if (field = AllocVec (sizeof (*field), MEMF_PUBLIC))
  563.    {
  564.       field->rp = rp;
  565.       field->left = left;
  566.       field->top = top;
  567.       field->rows = rows;
  568.       field->columns = columns;
  569.       field->mines = mines;
  570.       if ((field->data = AllocVec (rows * columns *
  571.                                    sizeof (*field->data), MEMF_PUBLIC)) &&
  572.           (field->zeros = AllocVec (rows * columns *
  573.                                     sizeof (*field->zeros), MEMF_PUBLIC)))
  574.       {
  575.          return field;
  576.       }
  577.    }
  578.    
  579.    field_free (field);
  580.    return NULL;
  581. }
  582.  
  583.  
  584. void
  585. field_free (
  586.    field_ptr   field)
  587. {
  588.    if (field != NULL)
  589.    {
  590.       if (field->data != NULL)
  591.          FreeVec (field->data);
  592.       if (field->zeros != NULL)
  593.          FreeVec (field->zeros);
  594.       
  595.       FreeVec (field);
  596.    }
  597. }
  598.  
  599.  
  600. WORD
  601. field_left (
  602.    field_ptr   field)
  603. {
  604.    return field->left;
  605. }
  606.  
  607.  
  608. WORD
  609. field_top (
  610.    field_ptr   field)
  611. {
  612.    return field->top;
  613. }
  614.  
  615.  
  616. BOOL
  617. field_size (
  618.    field_ptr   field,
  619.    UBYTE       rows,
  620.    UBYTE       columns,
  621.    UWORD       mines)
  622. {
  623.    field->mines = mines;
  624.    if (rows != field->rows || columns != field->columns)
  625.    {
  626.       FreeVec (field->data);
  627.       FreeVec (field->zeros);
  628.       if ((field->data = AllocVec (rows * columns *
  629.                                    sizeof (*field->data), MEMF_PUBLIC)) &&
  630.           (field->zeros = AllocVec (rows * columns *
  631.                                     sizeof (*field->zeros), MEMF_PUBLIC)))
  632.       {
  633.          field->rows = rows;
  634.          field->columns = columns;
  635.          
  636.          return TRUE;
  637.       }
  638.       
  639.       return FALSE;
  640.    }
  641.    
  642.    return TRUE;
  643. }
  644.  
  645.  
  646. BOOL
  647. field_inside (
  648.    field_ptr   field,
  649.    WORD        row,
  650.    WORD        column)
  651. {
  652.    return (BOOL)(row >= 0 && row < field->rows &&
  653.                  column >= 0 && column < field->columns);
  654. }
  655.  
  656.  
  657. BOOL
  658. field_reset (
  659.    field_ptr   field)
  660. {
  661.    STRPTR              msg;
  662.    ULONG               char_count;
  663.    struct TextExtent   te;
  664.    
  665.    UWORD            n = field->mines;
  666.    register UBYTE   r, c;
  667.    
  668.    
  669.    draw_box (field->rp, field_left (field), field_top (field),
  670.              FIELDWIDTH (field), FIELDHEIGHT (field), RECESSEDBOX);
  671.  
  672.    field->swept = 0;
  673.    memset (field->data, 0,
  674.            field->rows * field->columns * sizeof (*field->data));
  675.    
  676.    if (chosen_task == SWEEP_PATH && !generate_path (field))
  677.       return FALSE;
  678.    
  679.    SetAPen (field->rp, gui_pens[BACKGROUNDPEN]);
  680.    RectFill (field->rp, field->left + LINEWIDTH, field->top + LINEHEIGHT,
  681.              field->left + FIELDWIDTH (field) - LINEWIDTH - 1,
  682.              field->top + FIELDHEIGHT (field) - LINEHEIGHT - 1);
  683.    msg = localized_string (MSG_INITIALIZE_FIELD);
  684.    char_count = TextFit (field->rp, msg, strlen (msg), &te, NULL, 1,
  685.                          FIELDWIDTH (field) - 2 * LINEWIDTH,
  686.                          FIELDHEIGHT (field) - 2 * LINEHEIGHT);
  687.    SetAPen (field->rp, gui_pens[TEXTPEN]);
  688.    Move (field->rp,
  689.          field->left + (FIELDWIDTH (field) - te.te_Width) / 2,
  690.          field->top + field->rp->TxBaseline +
  691.          (FIELDHEIGHT (field) - te.te_Height) / 2);
  692.    Text (field->rp, msg, char_count);
  693.    
  694.    while (n > 0)
  695.    {
  696.       r = drand48 () * field->rows;
  697.       c = drand48 () * field->columns;
  698.       if (!(IS_MINE (field, r, c) ||
  699.             (IS_PATH (field, r, c) && chosen_task == SWEEP_PATH)))
  700.       {
  701.          SET_FIELD (field, r, c, MINE);
  702.          --n;
  703.       }
  704.    }
  705.    
  706.    for (r = 0; r < field->rows; ++r)
  707.       for (c = 0; c < field->columns; ++c)
  708.          if (!IS_MINE (field, r, c))
  709.             SET_FIELD (field, r, c, count_neighbors (field, r, c, MINE));
  710.    
  711.    SetAPen (field->rp, gui_pens[BACKGROUNDPEN]);
  712.    RectFill (field->rp, field->left + LINEWIDTH, field->top + LINEHEIGHT,
  713.              field->left + LINEWIDTH + cell_w - 1,
  714.              field->top + LINEHEIGHT + cell_h - 1);
  715.    draw_box (field->rp, field->left + LINEWIDTH, field->top + LINEHEIGHT,
  716.              cell_w, cell_h, RAISEDBOX);
  717.    for (r = 0; r < field->rows; ++r)
  718.    {
  719.       for (c = 0; c < field->columns; ++c)
  720.       {
  721.          if (r || c)
  722.          {
  723.             ClipBlit (field->rp,
  724.                       field->left + LINEWIDTH, field->top + LINEHEIGHT,
  725.                       field->rp, field->left + LINEWIDTH + c * cell_w,
  726.                       field->top + LINEHEIGHT + r * cell_h,
  727.                       cell_w, cell_h, 0xC0);
  728.          }
  729.       }
  730.    }
  731.    
  732.    if (auto_opening > 0 && chosen_task == SWEEP_ALL)
  733.       generate_opening (field);
  734.    else if (chosen_task == SWEEP_PATH)
  735.    {
  736.       avoid_trivial (field);
  737.       chosen_task = SWEEP_ALL;
  738.       reveal_this (field, 0, 0);
  739.       chosen_task = SWEEP_PATH;
  740.       time_on = TRUE;
  741.       timer_start (timer_obj, 0L, 1000000L);
  742.    }
  743.    
  744.    return TRUE;
  745. }
  746.  
  747.  
  748. void
  749. field_draw (
  750.    field_ptr   field)
  751. {
  752.    register UBYTE   r, c;
  753.    
  754.    
  755.    draw_box (field->rp, field_left (field), field_top (field),
  756.              FIELDWIDTH (field), FIELDHEIGHT (field), RECESSEDBOX);
  757.    for (r = 0; r < field->rows; ++r)
  758.    {
  759.       for (c = 0; c < field->columns; ++c)
  760.       {
  761.          draw_cell (field->rp, field->left + LINEWIDTH + c * cell_w,
  762.                     field->top + LINEHEIGHT + r * cell_h,
  763.                     FIELD (field, r, c));
  764.       }
  765.    }
  766. }
  767.  
  768.  
  769. void
  770. field_delete (
  771.    field_ptr   field)
  772. {
  773.    SetAPen (field->rp, gui_pens[BACKGROUNDPEN]);
  774.    RectFill (field->rp, field->left, field->top,
  775.              field->left + FIELDWIDTH (field) - 1,
  776.              field->top + FIELDHEIGHT (field) - 1);
  777. }
  778.  
  779.  
  780. BOOL
  781. reveal_this (
  782.    field_ptr   field,
  783.    UBYTE       row,
  784.    UBYTE       col)
  785. {
  786.    if (IS_SWEEPABLE (field, row, col) &&
  787.        (chosen_task == SWEEP_ALL || count_neighbors (field, row, col, SWEPT)))
  788.    {
  789.       SWEEP_CELL (field, row, col);
  790.       draw_cell (field->rp, field->left + LINEWIDTH + col * cell_w,
  791.                  field->top + LINEHEIGHT + row * cell_h,
  792.                  FIELD (field, row, col));
  793.       
  794.       if (IS_MINE (field, row, col))
  795.          return FALSE;
  796.       
  797.       ++field->swept;
  798.  
  799.       if (CELL_VAL (field, row, col) == 0)
  800.          sweep_zero (field, row, col);
  801.    }
  802.    
  803.    return TRUE;
  804. }
  805.  
  806.  
  807. BOOL
  808. sweep_this (
  809.    field_ptr   field,
  810.    UBYTE       row,
  811.    UBYTE       col)
  812. {
  813.    register WORD    r, c;
  814.    register UBYTE   lock_count = 0;
  815.    register UBYTE   warn_count = 0;
  816.    register UBYTE   free_count = 0;
  817.    register BOOL    success = TRUE;
  818.    
  819.    
  820.    if (IS_SWEPT (field, row, col))
  821.    {
  822.       for (r = row - 1; r <= row + 1; ++r)
  823.       {
  824.          for (c = col - 1; c <= col + 1; ++c)
  825.          {
  826.             if (field_inside (field, r, c))
  827.             {
  828.                if (IS_WARNED (field, r, c))
  829.                   ++warn_count;
  830.                else if (IS_LOCKED (field, r, c))
  831.                   ++lock_count;
  832.                else if (!IS_SWEPT (field, r, c))
  833.                   ++free_count;
  834.             }
  835.          }
  836.       }
  837.       
  838.       if (lock_count >= CELL_VAL (field, row, col))
  839.       {
  840.          if (warn_count)
  841.             mutate_neighbors (field, row, col, WARNED, WARNED | LOCKED);
  842.          success = reveal_around (field, row, col);
  843.       }
  844.       else if (free_count + warn_count + lock_count ==
  845.                CELL_VAL (field, row, col))
  846.       {
  847.          if (warn_count)
  848.             mutate_neighbors (field, row, col, WARNED, WARNED | LOCKED);
  849.          for (r = row - 1; r <= row + 1; ++r)
  850.          {
  851.             for (c = col - 1; c <= col + 1; ++c)
  852.             {
  853.                if (field_inside (field, r, c) && IS_SWEEPABLE (field, r, c))
  854.                {
  855.                   toggle_lock (field, r, c);
  856.                }
  857.             }
  858.          }
  859.       }
  860.    }
  861.    release_around (field, row, col);
  862.    
  863.    return success;
  864. }
  865.  
  866.  
  867. void
  868. toggle_lock (
  869.    field_ptr   field,
  870.    UBYTE       row,
  871.    UBYTE       col)
  872. {
  873.    if (!IS_SWEPT (field, row, col))
  874.    {
  875.       if (IS_LOCKED (field, row, col))
  876.       {
  877.          if (IS_WARNED (field, row, col))
  878.             UNLOCK_CELL (field, row, col);
  879.          else
  880.          {
  881.             if (place_warnings)
  882.                WARN_CELL (field, row, col);
  883.             else
  884.                UNLOCK_CELL (field, row, col);
  885.             counter_update (flag_counter, counter_value (flag_counter) + 1);
  886.          }
  887.          draw_cell (field->rp, field->left + LINEWIDTH + col * cell_w,
  888.                     field->top + LINEHEIGHT + row * cell_h,
  889.                     FIELD (field, row, col));
  890.       }
  891.       else if (counter_value (flag_counter) > 0)
  892.       {
  893.          LOCK_CELL (field, row, col);
  894.          draw_cell (field->rp, field->left + LINEWIDTH + col * cell_w,
  895.                     field->top + LINEHEIGHT + row * cell_h,
  896.                     FIELD (field, row, col));
  897.          counter_update (flag_counter, counter_value (flag_counter) - 1);
  898.       }
  899.    }
  900. }
  901.  
  902.  
  903. void
  904. press_this (
  905.    field_ptr   field,
  906.    UBYTE       row,
  907.    UBYTE       col)
  908. {
  909.    if (IS_SWEEPABLE (field, row, col) &&
  910.        (chosen_task == SWEEP_ALL || count_neighbors (field, row, col, SWEPT)))
  911.    {
  912.       draw_box (field->rp, field->left + LINEWIDTH + col * cell_w,
  913.                 field->top + LINEHEIGHT + row * cell_h,
  914.                 cell_w, cell_h, THINBOX);
  915.    }
  916. }
  917.  
  918.  
  919. void
  920. press_around (
  921.    field_ptr   field,
  922.    UBYTE       row,
  923.    UBYTE       col)
  924. {
  925.    register WORD   r, c;
  926.    
  927.    
  928.    if (IS_SWEPT (field, row, col))
  929.       for (r = row - 1; r <= row + 1; ++r)
  930.          for (c = col - 1; c <= col + 1; ++c)
  931.             if (field_inside (field, r, c) && (r != row || c != col))
  932.                press_this (field, r, c);
  933. }
  934.  
  935.  
  936. void
  937. release_this (
  938.    field_ptr   field,
  939.    UBYTE       row,
  940.    UBYTE       col)
  941. {
  942.    if (IS_SWEEPABLE (field, row, col))
  943.    {
  944.       draw_box (field->rp, field->left + LINEWIDTH + col * cell_w,
  945.                 field->top + LINEHEIGHT + row * cell_h,
  946.                 cell_w, cell_h, RAISEDBOX);
  947.    }
  948. }
  949.  
  950.  
  951. void
  952. release_around (
  953.    field_ptr   field,
  954.    UBYTE       row,
  955.    UBYTE       col)
  956. {
  957.    register WORD   r, c;
  958.    
  959.    
  960.    if (IS_SWEPT (field, row, col))
  961.       for (r = row - 1; r <= row + 1; ++r)
  962.          for (c = col - 1; c <= col + 1; ++c)
  963.             if (field_inside (field, r, c) && (r != row || c != col))
  964.                release_this (field, r, c);
  965. }
  966.  
  967.  
  968. BOOL
  969. field_swept (
  970.    field_ptr   field)
  971. {
  972.    return (BOOL)((chosen_task == SWEEP_ALL &&
  973.                   field->rows * field->columns - field->mines ==
  974.                   field->swept) ||
  975.                  (chosen_task == SWEEP_PATH &&
  976.                   IS_SWEPT (field, field->rows - 1, field->columns - 1)));
  977. }
  978.  
  979.  
  980. void
  981. field_win (
  982.    field_ptr   field)
  983. {
  984.    register UBYTE   r, c;
  985.    
  986.    
  987.    for (r = 0; r < field->rows; ++r)
  988.    {
  989.       for (c = 0; c < field->columns; ++c)
  990.       {
  991.          if (IS_MINE (field, r, c) &&
  992.              (IS_SWEEPABLE (field, r, c) || IS_WARNED (field, r, c)))
  993.          {
  994.             LOCK_CELL (field, r, c);
  995.             draw_cell (field->rp, field->left + LINEWIDTH + c * cell_w,
  996.                        field->top + LINEHEIGHT + r * cell_h,
  997.                        FIELD (field, r, c));
  998.          }
  999.       }
  1000.    }
  1001.    counter_update (flag_counter, 0);
  1002. }
  1003.  
  1004.  
  1005. void
  1006. field_lose (
  1007.    field_ptr   field)
  1008. {
  1009.    register UBYTE   r, c;
  1010.    
  1011.    
  1012.    for (r = 0; r < field->rows; ++r)
  1013.    {
  1014.       for (c = 0; c < field->columns; ++c)
  1015.       {
  1016.          if (IS_MINE (field, r, c) && !IS_SWEPT (field, r, c))
  1017.          {
  1018.             SWEEP_CELL (field, r, c);
  1019.             draw_cell (field->rp, field->left + LINEWIDTH + c * cell_w,
  1020.                        field->top + LINEHEIGHT + r * cell_h,
  1021.                        FIELD (field, r, c));
  1022.          }
  1023.       }
  1024.    }
  1025. }
  1026.